package org.novelhibernate.core;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.novelhibernate.configuration.ContextLoader;
import org.novelhibernate.core.connection.ConnectionFactory;
import org.novelhibernate.core.connection.DataSourceTransactionFactory;
import org.novelhibernate.core.transaction.Transaction;
import org.novelhibernate.core.transaction.TransactionImpl;
import org.novelhibernate.exception.NoPreboundTransactionException;
import org.novelhibernate.exception.NovelhibernateException;

public class JdbcTemplateImpl implements JdbcTemplate {

	private static final Log log = LogFactory.getLog(JdbcTemplateImpl.class);

	private ConnectionFactory factory;

	private static JdbcTemplateImpl template;

	public static JdbcTemplateImpl getInstance() {

		if(null == template)
			template = new JdbcTemplateImpl();
		
		return template;

	}

	public JdbcTemplateImpl(int source) {

		switch (source) {

		 case ConnectionFactory.DATASOURCE_DRIVEN:
			factory = DataSourceTransactionFactory.getInstance();
			break;

		 case ConnectionFactory.JDBCSOURCE_DRIVEN:
			factory = DataSourceTransactionFactory.getInstance();
			break;

		}

		template = this;
	}

	public JdbcTemplateImpl() {

		switch (ContextLoader.CHOSEN_DRIVEN_TYPE) {

		 case ConnectionFactory.DATASOURCE_DRIVEN:
			factory = DataSourceTransactionFactory.getInstance();
			break;

		 case ConnectionFactory.JDBCSOURCE_DRIVEN:
			factory = DataSourceTransactionFactory.getInstance();
			break;

		}

		template = this;

	}

	@Override
	public Transaction beginPlainTransaction(boolean autoCommit,
			int transactionIsolation, boolean isRecordResult)
			throws SQLException {

		log.debug("Create plain transaction start");
		Transaction tran = null;
		try {

			tran = factory.createTransaction(autoCommit, transactionIsolation,
					isRecordResult);

			// catch and record to log and throw to above tier
		} catch (SQLException e) {

			log.error("Create plain transaction start faild", e);
			throw e;
		}

		log.debug("Create plain transaction successfully");
		return tran;
	}

	@Override
	public Transaction beginConnectionRelatedTransaction(Connection conn,
			boolean isRecordResult) throws SQLException {

		log.debug("Create transaction by given connection start");
		Transaction tran = null;
		try {

			tran = new TransactionImpl(conn, isRecordResult);

			// catch and record to log and throw to above tier
		} catch (SQLException e) {

			log.error("Create transaction by given connection faild", e);
			throw e;
		}

		log.debug("Create transaction by given connection successfully");
		return tran;
	}

	@Override
	public Transaction beginSpringManagedTransaction(Object dataSource,
			boolean isRecordResult) throws Throwable {

		log.debug("Create transaction by given " + dataSource.getClass()
				+ " start");

		// can be either SessionHolder or ConnectionHolder
		Object value = Class
				.forName(
						"org.springframework.transaction.support.TransactionSynchronizationManager")
				.getMethod("getResource", new Class[] { Object.class }).invoke(
						null, dataSource);

		if (null == value)
			throw new NoPreboundTransactionException();

		Connection conn = null;

		// if the other framework manage transaction by using datasource
		// (jdbctemplate), connection is contained in a ConnectionHolder
		if (dataSource instanceof DataSource)
			conn = (Connection) value.getClass().getMethod("getConnection")
					.invoke(value);
		// otherwise this should be SessionFactory
		// connection can be got by session which contained in
		// a SessionHolder
		else if (Class.forName("org.hibernate.SessionFactory").isInstance(
				dataSource)) {

			Object session = value.getClass().getMethod("getSession").invoke(
					value);
			conn = (Connection) session.getClass().getMethod("connection")
					.invoke(session);
		} else
			throw new NovelhibernateException(
					"Unsupported source/factory type in beginSpringManagedTransaction");

		Transaction tran = null;
		try {

			tran = new TransactionImpl(conn, isRecordResult);

			// catch and record to log and throw to above tier
		} catch (SQLException e) {

			log.error("Create transaction by given " + dataSource.getClass()
					+ " faild", e);
			throw e;
		}

		log.debug("Create transaction by given " + dataSource.getClass()
				+ " successfully");
		return tran;

	}

}
